let SmartCar = undefined;
let JsColorModel = undefined;
let IsRecordDriveData = false;
let RecordDriveDatasIndex = 0;
let RecordDriveDatas = [];
let ColorImageModule = undefined;
const RecordDriveDatasMaxSize = 100;

function GenerateUniqueFilename() {
    // 获取当前时间戳
    const timestamp = Date.now();

    // 生成随机数作为后缀
    const randomNum = Math.floor(Math.random() * 10000);

    // 组合时间戳和随机数作为文件名
    const filename = `${timestamp}_${randomNum}`;

    return filename;
}

function SaveToFile(data, filename) {
    const blob = new Blob([data]);
    const url = URL.createObjectURL(blob);

    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);

    a.click();

    setTimeout(() => {
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }, 0);
}

function OpenCamera() {
    const video = document.getElementById("video");
    navigator.mediaDevices.getUserMedia({
        video: {}
    }).then((stream) => {
        if (video) {
            video.srcObject = stream;
            video.play();
        }
    }).catch(function (error) {
        console.error('无法打开摄像头：', error);
    });
}

function GetCameraData(width, height) {
    const img = document.getElementById("img");
    const video = document.getElementById("video");
    const canvas = document.getElementById("canvas");
    const context = canvas.getContext("2d");
    context.drawImage(img || video, 0, 0, width || 320, height || 240);
    const base64_image_data = canvas.toDataURL("image/jpeg");
    return base64_image_data;
}

window.addEventListener("load", function () {
    if (window.NPLJSInstance) {
        NPLJSInstance.OnMsg("SmartCar", async (msgdata, msgid) => {
            const data = GetSmartCarData(msgdata);
            NPLJSInstance.SendMsg("SmartCarReply", data, msgid);
        });
        NPLJSInstance.OnMsg("CameraSnap", (msgdata, msgid) => {
            return NPLJSInstance.SendMsg("CameraSnapReply", { data: [] }, msgid);
        });

        NPLJSInstance.OnMsg("CameraSnapData", async (msgdata, msgid) => {
            return NPLJSInstance.SendMsg("CameraSnapDataReply", { data: [] }, msgid);
        });
    }
});

function SendMoveCarData(data) {
    if (!window.NPLJSInstance) return;
    NPLJSInstance.SendMsg("MoveSmartCar", data);
}

const App = {
    data() {
        const all_classify_name = "全部分类";
        const colors = JsColorModel.GetClassifyList();
        const colors_size = colors.size();
        const color_classify_list = [];
        for (let i = 0; i < colors_size; i++) color_classify_list.push(colors.get(i));
        return {
            tabs_value: "dataset",
            // tabs_value: "auto_drive",
            model: "goods",
            image_width: 320,
            image_height: 240,
            camera_url: "http://192.168.4.1:81/stream",
            // camera_url: "",
            classify: "",
            filter_classify: all_classify_name,
            all_classify_image_list_map: {},
            all_classify_name: all_classify_name,
            classify_map: {},
            image: {
                data: "",
                classify: ""
            },
            image_index: -1,
            predict_percentage: 0,
            predict_classify: "",
            debug: true,
            roadsign_box_style: {
                "position": "absolute",
                "border": "1px solid black",
                "left": "0px",
                "top": "0px",
                "width": "0px",
                "height": "0px",
            },
            train_params: {
                batch: 4,
                epochs: 2000,
                random: true,
                loss: 0.0,
            },
            roadlinedatas: [],
            colordatas: [],
            color_classify_list: color_classify_list,
            selected_color_classify: "白色",
            map_name: "map",
            is_train: false,
            auto_drive_datas: [],
            drive_data:{},
            drive_data_index: 0,
        };
    },
    computed: {
        predict_btn_text() {
            if (this.predict_percentage == 0) return "预测";
            return `预测 分类 ${this.predict_classify} 概率 ${this.predict_percentage}%`;
        },
        filter_classify_image_list() {
            return this.GetClassifyImageList(this.filter_classify);
        },
        all_classify_image_list() {
            return this.GetClassifyImageList(this.all_classify_name);
        },
    },
    watch: {
        camera_url(newCameraUrl, oldCameraUrl) {
            if (newCameraUrl != "") return;
            OpenCamera();
        },
        image_index() {
            this.SwitchImageIndex();
        },
        selected_color_classify() {
            this.LoadColorData();
        },
        tabs_value() {
            this.LoadTabData();
            if (this.tabs_value == "dataset" || this.tabs_value == "auto_drive") {
                setTimeout(function() {
                    OpenCamera();
                }, 200);
            }
        },
    },
    beforeMount() {
    },
    mounted() {
        if (!this.camera_url) {
            OpenCamera();
        }
        this.LoadTrainData();
    },
    methods: {
        GetClassifyImageList(classify) {
            this.all_classify_image_list_map[classify] = this.all_classify_image_list_map[classify] || [];
            return this.all_classify_image_list_map[classify];
        },
        SwitchImageIndex() {
            const image_index = this.image_index;
            const image = this.filter_classify_image_list[image_index] || { classify: "", data: "", path: "" };
            if (!image.data && image.path) {
                image.data = SmartCar.GetBase64ImageData(image.path);
            }
            this.image = image;
            this.classify = this.image.classify;
            this.LoadTabData();
        },
        LoadTabData() {
            if (this.tabs_value == "color") {
                this.LoadColorData();
            } else if (this.tabs_value == "dataset") {
                this.LoadRoadSignRect();
                // this.LoadTrainData();
            } else if (this.tabs_value == "roadline") {
                this.LoadRoadLineData();
            }
        },
        LoadRoadSignRect() {
            // if (this.model != "roadsigns" || this.tabs_value != "dataset") return;
            const text = SmartCar.PredictBase64ImageRoadSignRect(this.image.data);
            // const text = ColorImageModule.GetRedColorRect(this.image.data);
            const datas = text.trim().split(" ");
            if (datas.length >= 4) {
                this.roadsign_box_style.left = parseInt(datas[0]);
                this.roadsign_box_style.top = parseInt(datas[1]);
                this.roadsign_box_style.width = parseInt(datas[2]);
                this.roadsign_box_style.height = parseInt(datas[3]);
            }
            else {
                this.roadsign_box_style.left = 0;
                this.roadsign_box_style.top = 0;
                this.roadsign_box_style.width = 0;
                this.roadsign_box_style.height = 0;
            }
        },
        LoadColorData() {
            if (!this.image.data) return;
            const text = SmartCar.PredictBase64ImageColor(this.image.data, this.selected_color_classify, this.map_name);
            const colordatas = text.trim().split(" ");
            this.colordatas = [];
            for (let i = 0; i < colordatas.length; i++) {
                const value = parseInt(colordatas[i]);
                const data = { value: value, selected: false, index: i };
                this.colordatas.push(data);
            }
        },
        LoadRoadLineData() {
            if (!this.image.data) return;
            const multiLineString = SmartCar.PredictRoadLineBase64ImageColor(this.image.data, this.map_name);
            // const lastLine = multiLineString.trim().split('\n').pop();
            // const text = multiLineString.replace(lastLine, '').trim();
            // const datas = text.trim().split(" ");
            const datas = multiLineString.trim().split(" ");
            this.roadlinedatas = [];
            for (let i = 0; i < datas.length; i++) {
                const value = parseInt(datas[i]);
                const data = { value: value, selected: false, index: i };
                this.roadlinedatas.push(data);
            }
            SmartCar.GetRoadLineClassifyBase64Image(this.image.data, this.map_name);
        },
        GetModel(model_name) {
            return SmartCar.GetJsModelProxy(model_name || this.model);
        },
        LoadTrainData() {
            let datas = this.GetModel().LoadTrainData();
            const keys = datas.keys();
            this.classify_map = {};
            this.all_classify_image_list_map = {};
            for (let i = 0; i < keys.size(); i++) {
                const key = keys.get(i);
                const obj = {
                    path: key,
                    classify: datas.get(key)
                }
                this.classify_map[obj.classify] = true;
                this.GetClassifyImageList(this.all_classify_name).push(obj);
                this.GetClassifyImageList(obj.classify).push(obj);
            }

            if (this.filter_classify_image_list.length > 0) {
                this.image_index = this.filter_classify_image_list.length - 1;
                this.SwitchImageIndex();
            }

            const classify_list = this.GetModel().GetClassifyList();
            const classify_list_size = classify_list.size();
            for (let i = 0; i < classify_list_size; i++) this.classify_map[classify_list.get(i)] = true;
        },
        AddTrainData(filepath, classify, data) {
            this.GetModel().AddTrainData(filepath, classify, data);
        },
        DeleteTrainData(filepath) {
            this.GetModel().DeleteTrainData(filepath);
        },
        SetTrainData(filepath, classify) {
            this.GetModel().SetTrainData(filepath, classify);
        },
        DoEpochTrainData() {
            return this.GetModel().DoEpoch();
        },
        ClickAddClassifyImage() {
            const data = GetCameraData(this.image_width, this.image_height);
            const classify = this.classify;
            const filename = GenerateUniqueFilename() + ".jpg";
            const filepath = this.GetModel().GetImageDirectory() + "/" + filename;
            this.classify_map[classify] = true;
            this.AddTrainData(filepath, classify, data);

            const obj = { classify: classify, data: data, path: filepath };
            this.all_classify_image_list.push(obj);
            this.GetClassifyImageList(classify).push(obj);
            this.filter_classify = classify;
            this.image_index = this.GetClassifyImageList(this.filter_classify).length - 1;
        },
        ClickDeleteClassifyImage() {
            const path = this.image.path;
            this.DeleteTrainData(path);
            this.all_classify_image_list_map[this.all_classify_name] = (this.all_classify_image_list_map[this.all_classify_name] || []).filter(item => item.path !== path);
            this.all_classify_image_list_map[this.image.classify] = (this.all_classify_image_list_map[this.image.classify] || []).filter(item => item.path !== path);
            this.image_index = this.image_index - 1;
            if (this.image_index < 0 && this.filter_classify_image_list.length > 0) {
                this.image_index = 0;
                this.SwitchImageIndex();
            }
        },
        OnImageClassifyChanged() {
            this.SetTrainData(this.image.path, this.image.classify);
            this.classify_map[this.image.classify] = true;
            for (let key in this.all_classify_image_list_map) {
                const list = this.all_classify_image_list_map[key];
                if (key != this.all_classify_name) {
                    this.all_classify_image_list_map[key] = (list || []).filter(item => item.classify == key);
                }
            }
        },
        ClickPredictBtn() {
            try {
                const data = GetCameraData(this.image_width, this.image_height);
                const predict = this.GetModel().Predict(data);
                const results = predict.split(" ");
                this.predict_classify = results[0];
                this.predict_percentage = parseFloat(results[1]) * 100;
            } catch (e) {
                console.log(e);
            }
        },
        OnCurrentChange(currentPage) {
            this.image_index = currentPage - 1;
        },
        DoEpoch() {
            if (!this.is_training) return;
            const loss = this.DoEpochTrainData();
            this.train_params.loss = `第${this.epoch}次训练, 损失值 ${loss.toFixed(5)}`;
            this.epoch = this.epoch + 1;
            if (isNaN(loss)) {
                return this.ClickStopTrain();
            }
            // if (loss < 0.0001 || this.epoch >= this.train_params.epochs) {
            if (this.epoch >= this.train_params.epochs) {
                this.GetModel().SaveModel();
                this.train_params.loss = `训练完成, 已保存训练结果, 训练${this.epoch}次 损失值 ${loss.toFixed(5)}`;
                this.is_training = false;
                ElementPlus.ElMessage({
                    message: '训练完成, 已保存训练结果',
                    type: 'success',
                    plain: true,
                });
                return;
            }
            requestAnimationFrame(this.DoEpoch);
        },
        ClickStartTrain() {
            if (this.is_training) return;
            this.epoch = 0;
            this.is_training = true;
            this.GetModel().SetTrainParams(parseInt(this.train_params.batch), this.train_params.random);
            this.DoEpoch();
        },
        ClickStopTrain() {
            this.is_training = false;
            this.train_params.loss = "训练终止, 训练结果丢弃, 损失: " + this.train_params.loss;
            ElementPlus.ElMessage({
                message: '训练终止, 训练结果丢弃',
                type: 'warning',
                plain: true,
            });
        },
        ClickDownloadTrainData() {
            SaveToFile(this.GetModel().JsDownloadDataSet(), this.model + ".zip");
        },
        ClickDeleteTrainModel() {
            this.GetModel().DeleteModel();
        },
        ClickColorFlag(data) {
            data.selected = !data.selected;
        },
        ClickTrainSelectedColor() {
            let text = "";
            for (let i = 0; i < this.colordatas.length; i++) {
                const data = this.colordatas[i];
                if (data.selected) {
                    text = text + i.toString() + " ";
                }
            }
            SmartCar.TrainBase64ImageColors(this.image.data, text, this.selected_color_classify, this.map_name);
        },
        ClickDownLoadColorDataSet() {
            SaveToFile(SmartCar.DownLoadColorDataSet(this.map_name), this.map_name + ".colors.dataset.colors");
        },
        ClickMoveForwardBtn() {
            if (this.is_train) {
                this.classify = "直行";
                this.ClickAddClassifyImage();
            }
            SendMoveCarData({ direction: "forward" });
        },
        ClickMoveBackwardBtn() {
            if (this.is_train) {
                this.classify = "后退";
                this.ClickAddClassifyImage();
            }
            SendMoveCarData({ direction: "backward" });
        },
        ClickMoveLeftBtn() {
            if (this.is_train) {
                this.classify = "左转";
                this.ClickAddClassifyImage();
            }
            SendMoveCarData({ direction: "left" });
        },
        ClickMoveRightBtn() {
            if (this.is_train) {
                this.classify = "右转";
                this.ClickAddClassifyImage();
            }
            SendMoveCarData({ direction: "right" });
        },
        ClickAutoMovedBtn() {
            SendMoveCarData({ direction: "auto" });

            if (this.tabs_value == "auto_drive") {
                RecordDriveDatas = [];
                RecordDriveDatasIndex = 0;
                IsRecordDriveData = true;
                clearInterval(this.refresh_record_drive_data_timer);
                this.refresh_record_drive_data_timer = setInterval(() => {
                    if (!IsRecordDriveData) return;
                    this.RefreshAutoDriveDatas();
                }, 1000);
            }
        },
        ClickStepAutoMovedBtn() {
            SendMoveCarData({ direction: "step_auto" });
        },
        ClickStopMoveBtn() {
            SendMoveCarData({ direction: "stop_move" });

            if (this.tabs_value == "auto_drive") {
                IsRecordDriveData = false;
                this.RefreshAutoDriveDatas();
                clearInterval(this.refresh_record_drive_data_timer);
            }
        },
        OnModelChanged() {
            this.LoadTrainData();
        },
        OnDriveDataIndexChange(currentPage) {
            this.drive_data_index = currentPage - 1;
            this.SwitchDriveDataIndex();
        },
        RefreshAutoDriveDatas() {
            this.auto_drive_datas = [];
            for (let i = 1; i <= RecordDriveDatasMaxSize; i++) {
                const index = (RecordDriveDatasIndex + i) % RecordDriveDatasMaxSize;
                if (RecordDriveDatas[index]) {
                    this.auto_drive_datas.push(RecordDriveDatas[index]);
                }
            }
            this.drive_data_index = this.auto_drive_datas.length - 1;
            this.SwitchDriveDataIndex();
        },
        SwitchDriveDataIndex() {
            this.drive_data = this.auto_drive_datas[this.drive_data_index] || {};
        },
        ClickAddDriveDataBtn() {
            console.log(GetSmartCarData({predict_roadline_near_direction: true, predict_roadline_far_direction: true}));
            this.RefreshAutoDriveDatas();
        },
        ClickNearRoadLineData() {
            const filename = GenerateUniqueFilename() + ".jpg";
            const model = this.GetModel("roadlines_near_direction");
            const filepath = model.GetImageDirectory() + "/" + filename;
            const data = this.drive_data;
            if (!data.image_data || !data.roadline_near_direction_label) return;
            this.AddTrainData(filepath, data.roadline_near_direction_label, data.image_data);
        },
        ClickFarRoadLineData() {
            const filename = GenerateUniqueFilename() + ".jpg";
            const model = this.GetModel("roadlines_far_direction");
            const filepath = model.GetImageDirectory() + "/" + filename;
            const data = this.drive_data;
            if (!data.image_data || !data.roadline_near_direction_label) return;
            this.AddTrainData(filepath, data.roadline_near_direction_label, data.image_data);
        },
    }
};

var Module = typeof Module != 'undefined' ? Module : {};
Module.onRuntimeInitialized = async function () {
    // ColorImageModule = await ColorImage();

    FS.mkdir('/darknet');
    FS.mount(IDBFS, { root: '/darknet' }, '/darknet');

    FS.syncfs(true, function (err) {
        console.log("加载IDBFS!!!");
        SmartCar = new Module.SmartCar();
        JsColorModel = Module.GetJsColorModelInstance();
        SmartCar.SetRootDirectory('/darknet');

        const app = Vue.createApp(App);
        app.use(ElementPlus);
        app.mount("#app");
    });
};


function GetSmartCarData(msgdata) {
    const replydata = { camera_id: this.m_camera_id };
    let imgdata = GetCameraData();
    if (!SmartCar) return replydata;
    // 预测路标
    if (msgdata.predict_roadsign) {
        const results = SmartCar.PredictBase64ImageRoadSign(imgdata).split(" ");
        replydata.label = results[0];
        replydata.probability = parseFloat(results[1]);
        // 推荐使用下面结果, 上面保留兼容lyx代码
        replydata.roadsign_label = results[0];
        replydata.roadsign_probability = parseFloat(results[1]);
        replydata.roadsign_x = parseInt(results[2]);
        replydata.roadsign_y = parseInt(results[3]);
        replydata.roadsign_w = parseInt(results[4]);
        replydata.roadsign_h = parseInt(results[5]);
        console.log(results, replydata.roadsign_x + replydata.roadsign_w / 2, replydata.roadsign_y + replydata.roadsign_h / 2);
    }
    // 预测路线偏转
    if (msgdata.predict_roadline) {
        const results = SmartCar.PredictBase64ImageRoadLine(imgdata).split(" ");
        replydata.roadline_label = results[0];
        replydata.roadline_probability = parseFloat(results[1]);
    }
    // 预测近路线方向 
    if (msgdata.predict_roadline_near_direction) {
        const results = SmartCar.PredictBase64ImageRoadLineNearDirection(imgdata).split(" ");
        replydata.roadline_near_direction_label = results[0];
        replydata.roadline_near_direction_probability = parseFloat(results[1]);
    }
    // 预测远路线方向 
    if (msgdata.predict_roadline_far_direction) {
        const results = SmartCar.PredictBase64ImageRoadLineFarDirection(imgdata).split(" ");
        replydata.roadline_far_direction_label = results[0];
        replydata.roadline_far_direction_probability = parseFloat(results[1]);
    }
    // 预测路线颜色数据 1 - 白色 0 - 黑色 -1 - 其它颜色(背景颜色)
    if (msgdata.predict_roadline_color_data) {
        replydata.roadline_color_data = SmartCar.PredictRoadLineBase64ImageColor(imgdata, msgdata.map_name || "none");
        // const multiLineString = SmartCar.PredictRoadLineBase64ImageColor(imgdata, msgdata.map_name || "none");
        // const lastLine = multiLineString.trim().split('\n').pop();
        // replydata.roadline_color_data = multiLineString.replace(lastLine, '').trim();
        // replydata.roadline_classify = lastLine;
    }
    if (msgdata.get_roadline_classify) {
        replydata.roadline_classify = SmartCar.GetRoadLineClassifyBase64Image(imgdata, msgdata.map_name || "map");
    }
    // 预测图像指定颜色数据
    if (msgdata.predict_image_color_data) {
        replydata.image_color_data = SmartCar.PredictBase64ImageColor(imgdata, msgdata.color || "白色", msgdata.map_name || "none");
    }
    if (msgdata.get_image_rgb565_data) {
        replydata.image_rgb565_data = SmartCar.GetBase64ImageRGB565Data(imgdata);
    }
    // 获取图像base64数据
    if (msgdata.get_image_data) {
        replydata.image_data = imgdata;
    }
    if (msgdata.get_roadsign_datas) {
        replydata.roadsign_datas = SmartCar.GetRoadSignImageRGBDatas(imgdata);
    }
    if (IsRecordDriveData && (msgdata.predict_roadline_near_direction || msgdata.predict_roadline_far_direction))
    {
        RecordDriveDatasIndex = RecordDriveDatasIndex % RecordDriveDatasMaxSize;
        RecordDriveDatas[RecordDriveDatasIndex++] = {
            image_data: imgdata,
            roadline_near_direction_label: replydata.roadline_near_direction_label,
            roadline_near_direction_probability: replydata.roadline_near_direction_probability,
            roadline_far_direction_label: replydata.roadline_far_direction_label,
            roadline_far_direction_probability: replydata.roadline_far_direction_probability,
        }
    }
    return replydata;
}

